package com.billlv.codegenerator.service.impl;

import com.billlv.codegenerator.common.utils.RedisUtility;
import com.billlv.codegenerator.domain.dto.UserDetailsDTO;
import com.billlv.codegenerator.domain.entity.UsersEntity;
import com.billlv.codegenerator.repository.UsersRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository userRepository;

    private static final String USER_CACHE_PREFIX = "user:details:"; // Redis缓存前缀
    private static final long CACHE_EXPIRE_TIME = 8*3600; // 缓存过期时间（秒）
    private static final ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private RedisUtility<UserDetailsDTO> redisUtility; // 用来操作 Redis

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从 Redis 中获取用户信息
        UserDetailsDTO cachedData = redisUtility.get(USER_CACHE_PREFIX + username, UserDetailsDTO.class);
        log.info("start auth {}",cachedData);
        try {
            String valueAsString = objectMapper.writeValueAsString(cachedData);
            log.info("auth read {}",valueAsString);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        log.info("end auth {}",cachedData);
        if (cachedData != null) {
            return reconstructUserDetails(cachedData);
        }

        synchronized (this) {
            // 再次检查缓存，防止并发问题
            cachedData = redisUtility.get(USER_CACHE_PREFIX + username,UserDetailsDTO.class);
            if (cachedData != null) {
                return reconstructUserDetails(cachedData);
            }

            // 从数据库中加载用户
            UsersEntity user = userRepository.findByUsername(username)
                    .orElseThrow(() -> new UsernameNotFoundException("用户名或密码错误"));
            log.info("start user {}",user);
            if (user.getDisabled()) {
                throw new UsernameNotFoundException("账户被禁用，请联系管理员！");
            }

//            Set<UserRole> userRoles = userRoleRepository.findByUserId(user.getId());
//            List<Role> roleList = roleRepository.findRolesByIds(userRoles.stream()
//                    .map(UserRole::getRoleId)
//                    .collect(Collectors.toList()));

            List<String> authorities =new ArrayList<>();
//                    roleList.stream()
//                    .map(Role::getRoleCode)
//                    .collect(Collectors.toList());

            // 构建 UserDetails 实例
            UserDetailsImpl userDetails = new UserDetailsImpl(
                    user.getId(),
                    user.getUsername(),
                    user.getPassword(),
                    authorities.stream()
                            .map(SimpleGrantedAuthority::new)
                            .collect(Collectors.toList())
            );

            // 保存到 Redis
            UserDetailsDTO cacheData = new UserDetailsDTO();
            cacheData.setUserId(user.getId());
            cacheData.setUsername(user.getUsername());
            cacheData.setPassword(user.getPassword());
            cacheData.setAuthorities(authorities);
            redisUtility.save(USER_CACHE_PREFIX + username, cacheData, CACHE_EXPIRE_TIME);

            return userDetails;
        }
    }

    /**
     * 从 Redis 缓存数据重建 UserDetailsImpl 对象
     */
    private UserDetailsImpl reconstructUserDetails(UserDetailsDTO cachedData) {
        Long userId = cachedData.getUserId();
        String username = cachedData.getUsername();
        String password = cachedData.getPassword();
        List<String> roles = cachedData.getAuthorities();

        List<GrantedAuthority> authorities = roles.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        return new UserDetailsImpl(userId, username, password, authorities);
    }
}
